/*
 * Copyright (c) 2010-2011, Petr Panteleyev <petr@panteleyev.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.gclient;

import com.google.common.collect.ImmutableList;
import com.google.gdata.data.media.mediarss.MediaThumbnail;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JMenu;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.html.HTML;
import org.panteleyev.blogclient.BloggerEditorFrame;
import org.panteleyev.blogclient.HandleExceptionEDT;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.photo.Thumbnail;
import org.panteleyev.swing.BasePanel;
import org.panteleyev.swing.BaseTable;
import org.panteleyev.swing.ClipboardUtil;
import org.panteleyev.swing.MenuProvider;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.swing.TableUtil;
import org.panteleyev.swing.ToolBarProvider;
import org.panteleyev.utilities.Updatable;

/**
 *
 * @author Petr Panteleyev <petr@panteleyev.org>
 */
public class PicasaGalleryPanel extends BasePanel<GPicasaGalleryFrame> implements Updatable, MenuProvider, ToolBarProvider {
    private AbstractTableModel  tableModel;
    private GBUserProfile       profile;
    private GBAccount           account;
    private ArrayList<GAlbum>   albums;

    private JMenu               menu;
    private ArrayList<AbstractAction> toolbarActions = new ArrayList<AbstractAction>();

    private BaseTable           table = new BaseTable();
    private JScrollPane         scroller = new JScrollPane(table);

    private SimpleDateFormat    _dateFormat = new SimpleDateFormat("dd.MM.yyyy");

    /* Actions */
    private AbstractAction      copyAlbumLinkAction;
    private AbstractAction      albumWebAction;
    private AbstractAction      propertiesAction;
    private AbstractAction      createAlbumAction;
    private AbstractAction      publishAlbumAction;

    private final String[]      picasaTableHeaderNames = {
        " ",
        L10N.PGF_TABLE_HEADER_TITLE.s(),
        L10N.PGF_TABLE_HEADERS_DESCR.s(),
        L10N.PGF_TABLE_HEADERS_LOCATION.s(),
        L10N.PGF_TABLE_HEADERS_DATE.s(),
        " ",
    };

    private static class FormattedDate extends Date {
        private DateFormat format;

        public FormattedDate(Date date, DateFormat format) {
            super(date.getTime());
            this.format = format;
        }

        @Override
        public String toString() {
            return format.format(this);
        }
    }

    private Runnable refreshTableEDT = new Runnable() {
        @Override
        public void run() {
            tableModel.fireTableDataChanged();
            TableUtil.adjustTableColumnSizes(table, 2, scroller);
        }
    };

    private Runnable endLongOperationEDT = new Runnable() {
        @Override
        public void run() {
            finishLongOperation();
        }
    };

    private final class UpdateThread extends Thread {
        UpdateThread() {
            startLongOperation();
        }

        @Override
        public void run() {
            try {
                account.reloadAlbums();
                albums = account.getAlbums();
                SwingUtilities.invokeLater(refreshTableEDT);
            } catch (Exception ex) {
                SwingUtilities.invokeLater(new HandleExceptionEDT(getFrame(), ex));
            } finally {
                SwingUtilities.invokeLater(endLongOperationEDT);
            }
        }
    }

    private abstract class AlbumThread extends Thread {
        private GAlbum album;

        AlbumThread(GAlbum album) {
            this.album = album;
            startLongOperation();
        }

        abstract void doInBackground() throws Exception;

        @Override
        public void run() {
            try {
                doInBackground();
                update();
            } catch (Exception ex) {
                SwingUtilities.invokeLater(new HandleExceptionEDT(getFrame(), ex));
            } finally {
                SwingUtilities.invokeLater(endLongOperationEDT);
            }
        }

        public GAlbum getAlbum() {
            return album;
        }
    }

    private class NewAlbumThread extends AlbumThread {
        NewAlbumThread(GAlbum album) {
            super(album);
        }

        @Override
        void doInBackground() throws Exception {
            account.addAlbum(getAlbum());
        }
    }

    private class AlbumPropertiesThread extends AlbumThread {
        AlbumPropertiesThread(GAlbum album) {
            super(album);
        }

        @Override
        void doInBackground() throws Exception {
            account.updateAlbum(getAlbum());
        }
    }

    public PicasaGalleryPanel(GPicasaGalleryFrame frame, GBUserProfile profile) {
        super(frame);
        this.profile = profile;

        account = profile.getAccount();
        albums = account.getAlbums();

        createActions();
        initComponents();
        createTableModel();
    }

    private void initComponents() {
        setLayout(new BorderLayout());

        add(scroller, BorderLayout.CENTER);

        table.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                if (evt.getClickCount() == 2) {
                    onTableDoubleClick();
                }
            }
        });
        scroller.addComponentListener(new java.awt.event.ComponentAdapter() {
            @Override
            public void componentResized(java.awt.event.ComponentEvent evt) {
                TableUtil.adjustTableColumnSizes(table, 2, scroller);
            }
        });

        scroller.setComponentPopupMenu(createMenu().getPopupMenu());

        table.setShowGrid(false);
        scroller.getViewport().setBackground(table.getBackground());
    }

    private void createActions() {
        propertiesAction = new AbstractAction(L10N.PGF_ALBUM_PROPS_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onProperties();
            }
        };

        copyAlbumLinkAction = new AbstractAction(L10N.PGF_COPY_LINK_ACTION.s(), Icon.COPY.i()) {
            @Override
            public void actionPerformed(ActionEvent evt) {
                onCopyAlbumLink();
            }
        };
        copyAlbumLinkAction.putValue(Action.LARGE_ICON_KEY, Icon.COPY.I());

        albumWebAction = new AbstractAction(L10N.PGF_ALBUM_WEB_ACTION.s(), Icon.EARTH.i()) {
            @Override
            public void actionPerformed(ActionEvent evt) {
                onAlbumWeb();
            }
        };
        albumWebAction.putValue(Action.LARGE_ICON_KEY, Icon.EARTH.I());

        createAlbumAction = new AbstractAction(L10N.PGF_CREATE_ALBUM_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(ActionEvent evt) {
                onNewAlbum();
            }
        };

        publishAlbumAction = new AbstractAction(L10N.ALBUM_FRAME_PUBLISH_PHOTOS_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                publishAlbum();
            }
        };

        toolbarActions.add(copyAlbumLinkAction);
        toolbarActions.add(albumWebAction);
    }

    @Override
    public JMenu createMenu() {
        return SwingFactory.createMenu("Album",
            propertiesAction,
            null,
            copyAlbumLinkAction,
            albumWebAction,
            publishAlbumAction,
            null,
            createAlbumAction);
    }

    @Override
    public JMenu getMenu() {
        return (menu == null) ? menu = createMenu() : menu;
    }

    private GAlbum getSelectedAlbum() {
        final int row = table.getSelectedRow();
        return (row == -1)? null : albums.get(table.convertRowIndexToModel(row));
    }

    private ImmutableList<GAlbum> getSelectedAlbums() {
        final int[] indexes = table.getSelectedRows();
        final ArrayList<GAlbum> selection = new ArrayList<GAlbum>(indexes.length);

        for (int ind : indexes) {
            selection.add(albums.get(table.convertRowIndexToModel(ind)));
        }

        return ImmutableList.copyOf(selection);
    }

    private String getLinkOfSelectedEntry() {
        int row = table.getSelectedRow();
        if (row != -1) {
            GAlbum album = albums.get(row);
            return (album == null)? null : album.getHtmlLinkAsString();
        } else {
            return null;
        }
    }

    private void onNewAlbum() {
        GAlbumDialog dial = new GAlbumDialog(getFrame(), null);
        if (dial.showDialog()) {
            final GAlbum album = new GAlbum();

            album.setTitle(dial.getAlbumTitle());
            album.setDescription(dial.getDescription());
            album.setLocation(dial.getAlbumLocation());
            album.setDateTaken(dial.getDateTaken());
            album.setPrivate(dial.isPrivate());

            new NewAlbumThread(album).start();
        }
    }

    private void onProperties() {
        final GAlbum album = getSelectedAlbum();
        if (album != null) {
            GAlbumDialog dial = new GAlbumDialog(getFrame(), album);
            if (dial.showDialog()) {
                album.setTitle(dial.getAlbumTitle());
                album.setDescription(dial.getDescription());
                album.setLocation(dial.getAlbumLocation());
                album.setDateTaken(dial.getDateTaken());
                album.setPrivate(dial.isPrivate());

                new AlbumPropertiesThread(album).start();
            }
        }
    }

    private void onCopyAlbumLink() {
        ClipboardUtil.put(getLinkOfSelectedEntry());
    }

    private void onAlbumWeb() {
        String url = getLinkOfSelectedEntry();
        if (url != null) {
            OSUtil.runBrowser(url);
        }
    }

    private void createTableModel() {
        tableModel = new AbstractTableModel() {
            @Override
            public int getColumnCount() { return picasaTableHeaderNames.length; }
            @Override
            public int getRowCount() { return albums.size(); }
            @Override
            public Object getValueAt(int row, int col) {
                GAlbum album = albums.get(row);

                switch (col) {
                    case 0:
                        return (album.isPrivate())? Icon.LOCK.i() : null;

                    case 1:
                        return album.getTitle();

                    case 2:
                        return album.getDescription();

                    case 3:
                        return album.getLocation();

                    case 4:
                        if (album.getDateTaken() != null) {
                            return new FormattedDate(album.getDateTaken(), _dateFormat);
                        } else {
                            return null;
                        }

                    case 5:
                        return album.getPhotoCount();

                    default:
                        return null;
                }
            }
            @Override
            public String getColumnName(int column) { return picasaTableHeaderNames[column]; }
            @Override
            public Class<?> getColumnClass(int column) {
                switch (column) {
                    case 0:
                        return ImageIcon.class;
                    case 4:
                        return Date.class;
                    case 5:
                        return Integer.class;
                    default:
                        return String.class;
                }
            }
        };

        table.setModel(tableModel);
        TableRowSorter sorter = new TableRowSorter<TableModel>(tableModel);
        sorter.setSortable(0, false);
        table.setRowSorter(sorter);
    }

    private void onTableDoubleClick() {
        GAlbum album = getSelectedAlbum();
        if (album != null) {
            getFrame().onOpenAlbum(album);
        }
    }

    @Override
    public void update() {
        new UpdateThread().start();
    }

    @Override
    public Collection<AbstractAction> getToolBarActions() {
        return toolbarActions;
    }

    private void startLongOperation() {
        getFrame().startLongOperation();
        propertiesAction.setEnabled(false);
        copyAlbumLinkAction.setEnabled(false);
        albumWebAction.setEnabled(false);
        createAlbumAction.setEnabled(false);
    }

    private void finishLongOperation() {
        propertiesAction.setEnabled(true);
        copyAlbumLinkAction.setEnabled(true);
        albumWebAction.setEnabled(true);
        createAlbumAction.setEnabled(true);
        getFrame().finishLongOperation();
    }

    private void publishAlbum() {
        ImmutableList<GAlbum> selection = getSelectedAlbums();
        if (selection.size() == 0) {
            return;
        }

        GAlbumPublishDialog dial = new GAlbumPublishDialog(null, profile, selection);
        if (dial.showDialog()) {
            try {
                getFrame().startLongOperation();

                StringBuilder b = new StringBuilder();

                if (dial.isUseTable()) {
                    b.append("<table");
                    if (dial.getTableBorder()) {
                        b.append(" border='1'");
                    }
                    b.append(">");
                }

                int nCols = Math.min(dial.getColumnCount(), selection.size());
                int nRows = selection.size() / nCols + (int)Math.signum(selection.size() % nCols);

                int index = 0;

                for (int row = 0; row < nRows; row++) {
                    if (index >= selection.size()) {
                        break;
                    }

                    if (dial.isUseTable()) {
                        b.append("<tr valign='top' align='center'>");
                    }

                    for (int col = 0; col < nCols; col++) {
                        if (index >= selection.size()) {
                            break;
                        }

                        final Thumbnail<GAlbum> thumbnail = dial.getThumbnail(index++);
                        final GAlbum set = thumbnail.getPayload();
                        final String setTitle = thumbnail.getTitle();

                        if (dial.isUseTable()) {
                            b.append("<td>");
                        }
                        b.append("<a href='")
                            .append(set.getHtmlLinkAsString())
                            .append("'>");

                        MediaThumbnail thmb = set.getThumbnail(dial.getSmallSizeIndex());

                        b.append("<img src='")
                            .append(thmb.getUrl())
                            .append("' width='")
                            .append(thmb.getWidth())
                            .append("' height='")
                            .append(thmb.getHeight())
                            .append("' border='");

                        if (dial.getImageBorder()) {
                            b.append("1'");
                        } else {
                            b.append("0'");
                        }

                        b.append("'>");
                        b.append("</a>");

                        if (dial.isUseTable()) {
                            if (dial.isAddTitle() && setTitle != null && !setTitle.isEmpty()) {
                                b.append("<br>");
                                b.append(setTitle);
                            }
                            b.append("</td>");
                        } else {
                            b.append("\n");
                        }
                    }

                    if (dial.isUseTable()) {
                        b.append("</tr>");
                    }
                }

                if (dial.isUseTable()) {
                    b.append("</table>");
                }

                BloggerEditorFrame<?> destWnd = dial.getDestinationWindow();
                if (destWnd == null) {
                    ClipboardUtil.put(b.toString());
                } else {
                    destWnd.getEditor().insertHTML(b.toString(), HTML.Tag.TABLE);
                }
            } catch (Exception ex) {
                MainClass.handleException(getFrame(), ex);
            } finally {
                getFrame().finishLongOperation();
            }
        }
    }
}
